package imse.team1.transportationorder.googlemapsapi;

import imse.team1.transportationorder.ws.TransportationPriceCalculationServiceSkeleton;
import org.xml.sax.InputSource;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

public class CalcDistFromOriginToDest {
    public final static String CACHEFILE = "webapps/axis2/prop_files/cache_distances-" + TransportationPriceCalculationServiceSkeleton.cacheFileId + ".properties";

    static {

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                saveCacheIntoFile();
            }
        });

        try {
            Properties prop = new Properties();
            InputStream in = FileInputStream.class.getResourceAsStream(CACHEFILE);
            if (in != null) { // there is a previously saved file
                prop.load(in);
                Iterator iter = prop.entrySet().iterator();
                while (true) {
                    if (!iter.hasNext())
                        break;
                    Map.Entry entry = (Map.Entry) iter.next();
                    CachedDistances.originToDestinationDistances.put(
                            (String) entry.getKey(), (String) entry.getValue());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

    public static Double calculateDistanceOrigToDest(String originCity, String destinationCity) {
        String distanceFromOriginToDestination = new String();
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();
            CustomizedSAXHandler1 handler = new CustomizedSAXHandler1();
            // 1. try to see if required distance is already in cache
            boolean checkCache = checkCache(
                    CachedDistances.originToDestinationDistances,
                    originCity, destinationCity);
            if (checkCache) {
                // retreive distances from origin to destination from cache
                distanceFromOriginToDestination = retrieveDistanceFromCache(
                        CachedDistances.originToDestinationDistances,
                        originCity, destinationCity);
            } else {
                // 2. if it is not in cache use Google Maps API to retrieve it
                // Create a URL for the desired page
                URL url = new URL(
                        "http://maps.googleapis.com/maps/api/distancematrix/xml?origins="
                                + originCity + "+Greece&destinations="
                                + destinationCity + "+Greece&mode=driving&language=en-EN&sensor=false");
                saxParser.parse(new InputSource(url.openStream()), handler);
                CachedDistances.originToDestinationDistances.put(originCity + "," + destinationCity, handler.returnDistance());
                //distanceFromOriginToDestination = retrieveDistanceFromCache(
                //	CachedDistances.retailersToWarehousesStoredDistances,
                //	originCity, destinationCity);
                distanceFromOriginToDestination = handler.returnDistance();
            }

        } catch (MalformedURLException e) {
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        Double distance;

        if (distanceFromOriginToDestination.trim().equals("1 m") || (distanceFromOriginToDestination.indexOf("km") == -1)) {
            distance = 0.00;
        } else {
            distance = Double.parseDouble(distanceFromOriginToDestination.substring(0, distanceFromOriginToDestination.indexOf(" km")));
        }

        return distance;
    }

    // retrieve warehouse names in order

    public static boolean checkCache(Map mp, String originName, String destinationName) {
        boolean checkCache = false;
        Iterator it = mp.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry) it.next();
            String keyValue = (String) pairs.getKey();
            String[] separated = keyValue.split(",");
            if (separated[0].equals(originName) && separated[1].equals(destinationName)) {
                checkCache = true;
                break;
            }
        }
        return checkCache;
    }

    public static void printMap(Map mp) {
        Iterator it = mp.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry) it.next();
            System.out.println(pairs.getKey() + " = " + pairs.getValue());
            it.remove(); // avoids a ConcurrentModificationException
        }
    }

    @SuppressWarnings("unchecked")
    public static String retrieveDistanceFromCache(Map mp, String originName, String destinationName) {
        String retrieveDistancesOrigToDest = new String();
        Iterator it = mp.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry) it.next();
            String keyValue = (String) pairs.getKey();
            String[] separated = keyValue.split(",");
            if (separated[0].equals(originName) && separated[1].equals(destinationName)) {
                retrieveDistancesOrigToDest = (String) pairs.getValue();
                break;
            }
            // it.remove(); // avoids a ConcurrentModificationException
        }
        return retrieveDistancesOrigToDest;
    }

    public static void saveCacheIntoFile() {
        Properties properties = new Properties();
        try {
            Iterator it = CachedDistances.originToDestinationDistances.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry pairs = (Map.Entry) it.next();
                properties.setProperty((String) pairs.getKey(), (String) pairs.getValue());
                it.remove(); // avoids a ConcurrentModificationException
            }
            File file = new File(CACHEFILE);
            if (!file.exists()){
                String rootHierarchy = CACHEFILE.substring(0, CACHEFILE.lastIndexOf("/"));
                new File(rootHierarchy).mkdirs();
                file.createNewFile();
            }
            FileOutputStream fileOut = new FileOutputStream(file);
            properties.store(fileOut, "Cached Distances");
            fileOut.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
